/*
 *
 *  * Copyright:  2018 smarabbit studio.
 *  *
 *  *  Licensed under the Confluent Community License; you may not use this file
 *  *  except in compliance with the License.  You may obtain a copy of the License at
 *  *
 *  *  http://www.confluent.io/confluent-community-license
 *  *
 *  *  Unless required by applicable law or agreed to in writing, software
 *  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  *  WARRANTIES OF ANY KIND, either express or implied.  See the License for the
 *  *  specific language governing permissions and limitations under the License.
 *  *
 *  *  @作   者： 黄开晖 (117227773@qq.com)
 *  *  @日   期:    ${DATE}
 *
 */

package com.massyframework.beanskin.runtime.service.instrument;

import org.slf4j.Logger;

import java.io.File;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Objects;

/**
 * 虚拟机工具
 */
abstract class VirtualMachineUtils {

    public static final String VM_CLASSNAME = "com.sun.tools.attach.VirtualMachine";
    private static volatile ClassLoader LOADER;
        
    /**
     * 附加Jars
     * 
     * @param agentJar {@link File}, 支持动态增强注入的代理jar
     * @param toolsJar {@link File}, jdk8的tools.jar
     * @param logger {@link Logger}, 日志记录器
     * @throws MalformedURLException 
     * @throws InvocationTargetException 
     * @throws IllegalArgumentException 
     * @throws IllegalAccessException 
     * @throws SecurityException 
     * @throws NoSuchMethodException 
     */
    public static void attachJars(File agentJar, File toolsJar, Logger logger)
    		throws MalformedURLException, IllegalAccessException, IllegalArgumentException, 
    		InvocationTargetException, NoSuchMethodException, SecurityException {
    	Objects.requireNonNull(toolsJar, "\"toolsJar\" cannot be null.");
    	Objects.requireNonNull(agentJar, "\"instrumentJar\" cannot be null.");
    	
    	Class<?> vmClass = getVirtualMatchineClass(toolsJar, logger);
    	if (vmClass == null) {
    		if (logger.isWarnEnabled()) {
    			logger.warn(String.format("load %s failed.", VM_CLASSNAME));
    		}
    		return ;
    	}
    	
    	RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
        String pid = runtime.getName();
        if (pid.indexOf("@") != -1)
            pid = pid.substring(0, pid.indexOf("@"));


        Method method = vmClass.getMethod("attach", new Class[] { String.class });
        Object vm = method.invoke(null, new Object[]{pid});

        // now deploy the actual agent, which will wind up calling agentmain()
        Method loadAgentMethod = vm.getClass().getMethod("loadAgent", new Class[] { String.class });
        if (loadAgentMethod != null){
            loadAgentMethod.invoke(vm, new Object[] { agentJar.getPath()});
            
            if (logger.isDebugEnabled()) {
            	logger.debug(String.format("attach %s success.", agentJar));
            }
        }
    }
    
    /**
     * 获取VirtualMachine类型
     * @param toolsJar {@link File}, tools.jar文件
     * @param logger  {@link Logger}, 日志记录器
     * @return {@link Class}
     * @throws MalformedURLException 
     */
    private static Class<?> getVirtualMatchineClass(File toolsJar, Logger logger) throws MalformedURLException{
    	Class<?> result = null;    	
    	if (result == null) {
    		result = retrieveVirtualMachineClass(
    				Thread.currentThread().getContextClassLoader());
    	}
    	
    	if (result == null) {
    		if (LOADER == null) {
    			LOADER = new URLClassLoader(
    					new URL[] {toolsJar.toURI().toURL()}, 
    						VirtualMachineUtils.class.getClassLoader());
    			if (logger.isTraceEnabled()) {
    				logger.trace("load tools.jar success.");
    			}
    		}
    		result = retrieveVirtualMachineClass(LOADER);
    	}
    		
    	return result;
    }
    
    /**
     * 取回VirtualMachine类型
     * @param classLoader {@link ClassLoader},类加载器
     * @return {@link Class}
     */
    private static Class<?> retrieveVirtualMachineClass(ClassLoader classLoader){
    	Class<?> result = null;    	
    	try{
            if (result == null){
            	result = classLoader.loadClass(VM_CLASSNAME);
            }
        }catch(Exception e){

        }
    	    	
    	return result;
    }

}
